#define  _STORAGE_GLOBAL_
#include "config.h"
#include <string.h>

//20160216 change to english
//20160216 when read error erase flash
//20160217 bug fix,reduce flash write JQilin
//20160329 bug fix,storageWriteData address align 4

// storage format
// 4byte(high2byte CRC + low2byte LEN)--------struct size align 4byte--------4byte ID
// 4 + align4byte + 4

RESTORE_DATA restoreData;


BOOL ensureEmptyArea(U32 *add,U32 size)
{
	U32 i;
	for (i = 0;i < size;i+=4)
	{
		if (*add != 0xFFFFFFFF)
		{
			return FALSE;
		}
		add++;
	}

	return TRUE;
}

U32 align4Byte(U32 size)
{
	return (size+3)/4*4;
}

U16 getDataSize(U32 add)
{
	return (*(U32 *)add)&0x0000FFFF;
}

U32 structStorageFormatSize(U32 structSize)
{
	return 4 + align4Byte(structSize) + 4;
}

U8* getDataAddress(U32 add)
{
	return (U8 *)(add+4);
}

U16 getDataCRC(U32 add)
{
	return (*(U32 *)add)>>16;
}

U16 calcDataCRC(U8 *pAdd,U32 len)
{
	U16 crcValue;

	crcValue = 0;
	while (len)
	{
		crcValue += *pAdd;
		pAdd++;
		len--;
	}

	return crcValue;
}

U32 getDataID(U32 add)
{
	return *(U32 *)(add+structStorageFormatSize(getDataSize(add))-4);
}

void storageEraseFlash(U32 addStart,U32 addSize)
{
	//flash operation
	FLASH_Unlock();//unlock
	FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);

	while (addSize)
	{
		FLASH_ErasePage(addStart);
		addStart += FLASH_SECTOR_SIZE;
		addSize -= FLASH_SECTOR_SIZE;
	}
	
	FLASH_Lock();//lock
}

U32 storageFindLastData(U32 addStart,U32 addSize,U32 ID)
{
	U32 returnADD = 0;
	U32 retOffset = 0;
	U32 getSize;

	while (1)
	{
		if (retOffset >= addSize)//start address out of range
		{
			break;
		}
		
		getSize = getDataSize(addStart+retOffset);
		if (0xFFFF == getSize || 0 == getSize)//invalid data
		{
			break;
		}

		if (retOffset+structStorageFormatSize(getSize) > addSize)//data struct out of range
		{
			break;
		}
		
		if (ID == getDataID(addStart+retOffset) && getDataCRC(addStart+retOffset) == calcDataCRC(getDataAddress(addStart+retOffset),getSize))
		{
			returnADD = addStart+retOffset;
		}
		else//error data
		{
			storageEraseFlash(INNER_FLASH_STORAGE_START,INNER_FLASH_STORAGE_SIZE);
			returnADD = 0;
			break;
		}
		retOffset += structStorageFormatSize(getSize);
	}

	return returnADD;
}

U32 storageFindEmptyArea(U32 addStart,U32 addSize,U32 fileSize,U32 ID)
{
	U32 lastStart = storageFindLastData(addStart,addSize,ID);

	if (lastStart)//have valid data
	{
		lastStart += structStorageFormatSize(getDataSize(lastStart));//move to empty
	}
	else
	{
		lastStart = addStart;//move to head
	}

	if (lastStart+structStorageFormatSize(fileSize) > addStart+addSize)//out range
	{
		return 0;
	}
	
	if (ensureEmptyArea((U32 *)lastStart,structStorageFormatSize(fileSize)))
	{
		return lastStart;
	}

	return 0;
}

U32 forU8ToU32LSB(U8 *p)
{
	U32 iTemp = 0;
	iTemp = (p[3] << 24) + (p[2] << 16) + (p[1] << 8) + p[0];
	return iTemp;
}

void storageWriteData(U32 addStart,U32 addSize,U8 *p,U32 len,U32 ID)
{
	U32 findAdd;
	U32 dataU32;
	U32 i;

	findAdd = storageFindLastData(addStart,addSize,ID);
	if (findAdd && len == getDataSize(findAdd))//check the same
	{
		if (0 == memcmp(getDataAddress(findAdd),p,len))
		{
			return;
		}
	}

	findAdd = storageFindEmptyArea(addStart,addSize,len,ID);
	if (!findAdd)
	{
		storageEraseFlash(addStart,addSize);
		findAdd = addStart;
	}

	//flash operation
	FLASH_Unlock();//unlock
	FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);	

	FLASH_ProgramWord(findAdd,len|(calcDataCRC(p,len)<<16));//crc + len
	findAdd += 4;

	for (i = 0;i < len;i+=4)//data
	{
		dataU32 = forU8ToU32LSB(p);
		p+=4;
		FLASH_ProgramWord(findAdd,dataU32);
		findAdd += 4;
	}

	FLASH_ProgramWord(findAdd,ID);//ID
	findAdd += 4;
	
	FLASH_Lock();//lock
}

void storageWrite(void)
{
	storageWriteData(INNER_FLASH_STORAGE_START,INNER_FLASH_STORAGE_SIZE,(U8 *)&restoreData
	,sizeof(RESTORE_DATA)
	,ID_FOR_STORAGE);
}

void copyDataToOthers(void)
{

}

void storageFactory(void)
{


	storageWrite();
}

void storageRead(void)
{
	U32 addFind;

	memset(&restoreData,0,sizeof(RESTORE_DATA));//clear struct
	
	if(restoreData.CarTireFL==0 || restoreData.CarTireFR==0 || restoreData.CarTireRL==0 || restoreData.CarTireRR==0)
	{
		restoreData.CarTireFL=0;
		restoreData.CarTireFR=1;
		restoreData.CarTireRL=2;
		restoreData.CarTireRR=3;
	}
	
	addFind = storageFindLastData(INNER_FLASH_STORAGE_START,INNER_FLASH_STORAGE_SIZE,ID_FOR_STORAGE);//find
	if (addFind && getDataSize(addFind) == sizeof(RESTORE_DATA))
	{
		memcpy(&restoreData,getDataAddress(addFind),getDataSize(addFind));
	}
	else//init struct when have not read correctly
	{
		storageFactory();
	}

	copyDataToOthers();
}

void storageTest(void)
{
#if 0
	U32 i;
	for (i = 0;i < 4096;i++)
	{
		storageRead();
		restoreData.haveBoxAID++;
		storageWrite();
		storageWrite();
	}
#endif
}

